﻿using MyCode.Project.Infrastructure.Common;
using MyCode.Project.Infrastructure.Utils;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using Wolf.ExternalService.CMBPay.Common;
using Wolf.ExternalService.CMBPay.Core;
using Wolf.ExternalService.CMBPay.Core.Events;
using Wolf.ExternalService.CMBPay.Core.Utils;
using static MyCode.Project.Infrastructure.Constant.LxmConst;



namespace MyCode.Project.Infrastructure.PayModels
{
    /// <summary>
    /// 招行支付的底层类扩展， 
    /// 方法1 增加查询订单实际支付情况的接口
    /// </summary>
    public class ExtendedGateway : Gateway
    {
        #region 字段

        /// <summary>
        /// 统一下单 接口类型
        /// </summary>
        private const string UnifiedOrderApiType = "unified.trade.micropay";

        /// <summary>
        /// 查询订单 接口类型
        /// </summary>
        private const string QueryApiType = "unified.trade.query";

        /// <summary>
        /// 关闭订单 接口类型
        /// </summary>
        private const string CloseOrderApiType = "unified.micropay.reverse";

        #endregion

        #region 属性



        /// <summary>
        /// 通知
        /// </summary>
        public new Notify Notify => (Notify)base.Notify;

        #endregion

        #region 构造函数

        /// <summary>
        /// 初始化一个<see cref="ExtendedGateway"/>类型的实例
        /// </summary>
        public ExtendedGateway() : this(new Merchant())
        {
        }

        /// <summary>
        /// 初始化一个<see cref="ExtendedGateway"/>类型的实例
        /// </summary>
        /// <param name="merchant">商家数据</param>
        public ExtendedGateway(Merchant merchant) : base(merchant)
        {
        }

        #endregion

        #region 查询订单

        /// <summary>
        /// 每隔5秒轮询判断用户各种支付的真实支付情况，总共轮询5次
        /// </summary>
        /// <param name="auxiliary">辅助接口</param>
        private void PollQueryTradeState(IAuxiliary auxiliary)
        {
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(5000);
                BuildQuery(auxiliary);
                string dsds = Notify.TradeState.ToLower();
                if (IsSuccessPay && Notify.TradeState.ToLower() == SUCCESS)
                {
                    OnPaymentSucceed(new PaymentSucceedEventArgs(this));
                    return;
                }
            }
        }



        /// <summary>
        /// 异步每隔5秒轮询判断用户是否支付，总共轮询5次
        /// </summary>
        /// <param name="auxiliary">辅助接口</param>
        /// <returns></returns>
        private async Task PollQueryTradeStateAsync(IAuxiliary auxiliary)
        {
            await Task.Run(() => PollQueryTradeState(auxiliary));
        }

        /// <summary>
        /// 查询订单的相关平台参数
        /// </summary>
        /// <param name="query"></param>
        public void QueryOrderPay(Auxiliary query)
        {

            Task.Run(async () =>
            {
                await PollQueryTradeStateAsync(new Auxiliary()
                {
                    TransactionId = query.TransactionId,
                    OutTradeNo = query.OutTradeNo
                });
            })
               .GetAwaiter()
               .GetResult();

        }
        /// <summary>
        /// 生成查询订单参数
        /// </summary>
        /// <param name="auxiliary">辅助参数</param>
        /// <returns></returns>
        private INotify BuildQuery(IAuxiliary auxiliary)
        {
            InitQuery(auxiliary);
            Commit();
            return Notify;
        }

        /// <summary>
        /// 初始化查询订单参数
        /// </summary>
        /// <param name="auxiliary">辅助参数</param>
        private void InitQuery(IAuxiliary auxiliary)
        {
            Merchant.Service = QueryApiType;
            InitAuxiliaryParameter(GatewayAuxiliaryType.Query, auxiliary);
        }

        #endregion


        /// <summary>
        /// 初始化订单参数
        /// </summary>
        /// <param name="action">操作</param>
        private void InitOrderParameter(Action action = null)
        {
            GatewayData.Clear();

            Merchant.NonceStr = Util.GenerateNonceStr();
            Merchant.DeviceInfo = Wolf.ExternalService.CMBPay.Common.Constant.WEB;

            action?.Invoke();

            GatewayData.Add(Merchant, StringCase.Snake);
            GatewayData.Add(Order, StringCase.Snake);
            AddSign();
        }

        /// <summary>
        /// 初始化辅助参数
        /// </summary>
        /// <param name="gatewayAuxiliaryType">辅助类型</param>
        /// <param name="auxiliary">辅助参数</param>
        private void InitAuxiliaryParameter(GatewayAuxiliaryType gatewayAuxiliaryType, IAuxiliary auxiliary)
        {
            auxiliary.Validate(gatewayAuxiliaryType);
            Merchant.NonceStr = Util.GenerateNonceStr();
            //Merchant.NotifyUrl = "";

            GatewayData.Add(Merchant, StringCase.Snake);
            GatewayData.Add(auxiliary, StringCase.Snake);
            AddSign();
        }

        /// <summary>
        /// 转换通知对象
        /// </summary>
        /// <returns></returns>
        protected override INotify ToObject()
        {
            return GatewayData.ToObject<Notify>(StringCase.Snake);
        }

        /// <summary>
        /// 转换通知对象
        /// </summary>
        /// <returns></returns>
        protected override async Task<INotify> ToObjectAsync()
        {
            return await GatewayData.ToObjectAsync<Notify>(StringCase.Snake);
        }

        #region testfuyoupay(富友扫客户支付码支付)
        /// <summary>
        /// 富友扫客户支付码支付
        /// </summary>
        /// <param name="order"></param>     
        /// <param name="order_type">支付类型 ALIPAY , WECHAT, QQ(QQ 钱包), UNIONPAY(银联二维码） , BESTPAY(翼支付)</param>     
        /// <returns></returns>
        public string FuiouScanpPay(Wolf.ExternalService.CMBPay.Barcodepay.Order order, string order_type)
        {
            Dictionary<string, string> map = new Dictionary<string, string>();
            map.Add("version", "1.0");
            map.Add("ins_cd", FuiouPayConst.ins_cd);
            map.Add("mchnt_cd", order.OpUserId);
            map.Add("term_id", FuiouPayConst.term_id);
            map.Add("random_str", Guid.NewGuid().ToString("N"));
            map.Add("sign", "");
            map.Add("order_type", order_type);
            map.Add("goods_des", order.Body);
            map.Add("goods_detail", "");
            map.Add("reserved_expire_minute", "1440");
            map.Add("addn_inf", order.Attach);
            map.Add("auth_code", order.AuthCode);
            map.Add("mchnt_order_no", order.OutTradeNo);
            map.Add("curr_type", "");
            map.Add("order_amt", order.Amount.ToString());
            map.Add("term_ip", FuiouPayConst.term_ip);
            map.Add("txn_begin_ts", DateTime.Now.ToString("yyyyMMddhhmmss"));
            map.Add("goods_tag", "");
            map.Add("sence", "1");
            map.Add("reserved_sub_appid", "");
            map.Add("reserved_limit_pay", "");
            string result = run(map, FuiouPayConst.fuiouScanpPayUrl);
            return result;
        }
        #endregion

        private static string run(Dictionary<string, string> map, string url)
        {
            Dictionary<string, string> reqs = new Dictionary<string, string>();
            Dictionary<string, string> nvs = new Dictionary<string, string>();

            foreach (KeyValuePair<string, string> kvp in map)
            {
                reqs.Add(kvp.Key, kvp.Value);
            }

            string sign = FuYouPayUtils.getSign(reqs);
            reqs.Remove("sign");
            reqs.Add("sign", sign);
            XmlDocument doc = new XmlDocument();
            XmlElement elem = doc.CreateElement("xml");
            doc.AppendChild(elem);

            foreach (KeyValuePair<string, string> kvp in reqs)
            {
                XmlElement elem2 = doc.CreateElement(kvp.Key);
                elem2.InnerText = kvp.Value;
                elem.AppendChild(elem2);
            }

            string reqBody = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?>" + doc.OuterXml;
            LogHelper.Info("请求报文：" + reqBody);
            reqBody = FuYouPayUtils.UrlEncode(reqBody, Encoding.GetEncoding(FuiouPayConst.charset));
            nvs.Add("req", reqBody);
          
            StringBuilder result = new StringBuilder("");
            Infrastructure.Utils.HttpUtils httpUtils = new HttpUtils();
            httpUtils.post(url, nvs, result);
            string data = HttpUtility.UrlDecode(result.ToString(), Encoding.GetEncoding(FuiouPayConst.charset));
            LogHelper.Info("在线支付返回报文:" + data);
            return data;
           
        }

        #region FuyouOnlinePay(富友客户扫商家的二维码支付)
        /// <summary>
        /// 富友客户扫商家的二维码支付
        /// </summary>
        /// <param name="order"></param>     
        /// <param name="order_type">支付类型 ALIPAY , WECHAT, QQ(QQ 钱包), UNIONPAY(银联二维码） , BESTPAY(翼支付)</param>     
        /// <returns></returns>
        public string FuiouOnlinePay(FuiouOrder order, string order_type)
        {
            Dictionary<string, string> map = new Dictionary<string, string>();
            map.Add("order_type", order_type);
            map.Add("goods_des", order.Body);
            map.Add("reserved_expire_minute", "1440");
            map.Add("addn_inf", order.Attach);
            map.Add("mchnt_order_no", order.OutTradeNo);
            map.Add("order_amt", order.Amount.ToString());
            map.Add("version", "1.0");
            map.Add("ins_cd", FuiouPayConst.ins_cd);
            map.Add("mchnt_cd", order.OpUserId);
            //map.Add("mchnt_cd", FuiouPayConst.mchnt_cd);
            map.Add("term_id", FuiouPayConst.term_id);
            map.Add("random_str", Guid.NewGuid().ToString("N"));
            map.Add("sign", "");
            map.Add("goods_detail", "");
            map.Add("curr_type", "");
            map.Add("term_ip", order.SpbillCreateIp);//FuiouPayConst.term_ip);
            map.Add("txn_begin_ts", DateTime.Now.ToString("yyyyMMddhhmmss"));
            map.Add("goods_tag", "");
            map.Add("notify_url", FuiouPayConst.notify_url);
            map.Add("reserved_sub_appid", "");
            map.Add("reserved_limit_pay", "");
            map.Add("reserved_terminal_info", order.DeviceInfo);
            string result = run(map, FuiouPayConst.fuiouOnlinePayUrl);
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(result);

            string ifok = doc.SelectSingleNode("/xml/result_code").InnerText;
            if (ifok == "000000")
                result = doc.SelectSingleNode("/xml/qr_code").InnerText;
            else
                result = doc.SelectSingleNode("/xml/result_msg").InnerText;
            return result;
        }
        #endregion

        /// <summary>
        /// 解析富友的XML字符串为可读文本
        /// </summary>
        /// <param name="res"></param>
        /// <returns></returns>
        public string GetXMLString(string res)
        {
            res = HttpUtility.UrlDecode(res, Encoding.GetEncoding(FuiouPayConst.charset));
            return res;
        }

        #region FuyouWXOnlinePay(富友 公众号/服务窗统一下单)
        /// <summary>
        /// 富友 公众号/服务窗统一下单
        /// </summary>
        /// <param name="order"></param>
        /// <param name="order_type">支付类型 ALIPAY , JSAPI, QQ(QQ 钱包), UNIONPAY(银联二维码） , BESTPAY(翼支付) LETPAY-小程序</param>
        /// <param name="openid"></param>
        /// <param name="appid"></param>
        /// <returns></returns>
        public string FuyouWXOnlinePay(Wolf.ExternalService.CMBPay.Barcodepay.Order order, string order_type, string openid, string appid)
        {
            
            Dictionary<string, string> map = new Dictionary<string, string>();
            map.Add("version", "1.0");
            map.Add("ins_cd", FuiouPayConst.ins_cd);
            map.Add("mchnt_cd", order.OpUserId);
            map.Add("term_id", FuiouPayConst.term_id);
            map.Add("random_str", Guid.NewGuid().ToString("N"));
            map.Add("sign", "");
            map.Add("goods_des", order.Body);
            map.Add("goods_detail", "");
            map.Add("goods_tag", "");
            map.Add("txn_begin_ts", DateTime.Now.ToString("yyyyMMddhhmmss"));
            map.Add("notify_url", FuiouPayConst.notify_url);
            map.Add("limit_pay", "");
            if (order_type == "WECHAT")
                order_type = "JSAPI";
            else if (order_type == "ALIPAY")
                order_type = "FWC";
            else if (order_type == "LETPAY")
                order_type = "LETPAY";
            map.Add("trade_type", order_type);
            map.Add("addn_inf", order.Attach);
            map.Add("openid", "");
            map.Add("product_id", "");
            map.Add("sub_openid", openid);
            map.Add("sub_appid", appid);
            map.Add("reserved_expire_minute", "1440");
            map.Add("mchnt_order_no", order.OutTradeNo);
            map.Add("curr_type", "CNY");
            map.Add("order_amt", order.Amount.ToString());
            map.Add("term_ip", FuiouPayConst.term_ip);

            string result = run(map, FuiouPayConst.fuiouOnlineWxPayUrl);
            LogHelper.Info(result);
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(result);

            string ifok = doc.SelectSingleNode("/xml/result_code").InnerText;
            if (ifok == "000000")
                result = doc.SelectSingleNode("/xml/reserved_pay_info").InnerText;
            else
                result = doc.SelectSingleNode("/xml/result_msg").InnerText;
            return result;
        }
        #endregion

        #region FuyouOnlinePay(富友主动查询订单支付状态)
        /// <summary>
        /// 富友主动查询订单支付状态
        /// </summary>
        /// <param name="mchnt_order_no">商户订单交易号</param>     
        /// <param name="order_type">支付类型 ALIPAY , WECHAT, QQ(QQ 钱包), UNIONPAY(银联二维码） , BESTPAY(翼支付)</param>     
        /// <returns></returns>
        public string FuyouQueryPay(string mchnt_order_no, string order_type, string mchnt_cd = "0002900F0370588")
        {
            Dictionary<string, string> map = new Dictionary<string, string>();
            map.Add("order_type", order_type);
            map.Add("mchnt_order_no", mchnt_order_no);
            map.Add("version", "1.0");
            map.Add("ins_cd", FuiouPayConst.ins_cd);
            map.Add("mchnt_cd", mchnt_cd);
            map.Add("term_id", FuiouPayConst.term_id);
            map.Add("random_str", Guid.NewGuid().ToString("N"));
            map.Add("sign", "");

            string result = run(map, FuiouPayConst.fuiou_23_url);
            //XmlDocument doc = new XmlDocument();
            //doc.LoadXml(result);

            //string ifok = doc.SelectSingleNode("/xml/result_code").InnerText;
            //if (ifok == "000000")
            //    result = doc.SelectSingleNode("/xml/trans_stat").InnerText;
            //else
            //    result = doc.SelectSingleNode("/xml/result_msg").InnerText;
            return result;
        }
        #endregion

        #region FuYouReturnMoney(富友退款申请)
        /// <summary>
        /// 富友退款申请
        /// </summary>
        public string FuYouReturnMoney(string OutTradeNo, string order_type, string refund_order_no
            , decimal total_amt, decimal refund_amt, string mchnt_cd = "")
        {
            string total = (total_amt * 100).ToString("0");
            string refund = (refund_amt * 100).ToString("0");
            Dictionary<string, string> map = new Dictionary<string, string>();
            map.Add("version", "1.0");
            map.Add("ins_cd", FuiouPayConst.ins_cd);
            map.Add("mchnt_cd", mchnt_cd);
            map.Add("term_id", FuiouPayConst.term_id);
            map.Add("random_str", Guid.NewGuid().ToString("N"));
            map.Add("sign", "");
            map.Add("order_type", order_type);
            map.Add("mchnt_order_no", OutTradeNo);
            map.Add("total_amt", total);
            map.Add("operator_id", "");
            map.Add("reserved_fy_term_id", "");
            map.Add("refund_amt", refund);
            map.Add("refund_order_no", refund_order_no);

            string result = run(map, FuiouPayConst.fuiou_24_url);
            result = GetXMLString(result);
            //XmlDocument doc = new XmlDocument();
            //doc.LoadXml(result);

            //string ifok = doc.SelectSingleNode("/xml/result_code").InnerText;
            //if (ifok == "000000")
            //    result = doc.SelectSingleNode("/xml/reserved_refund_amt").InnerText;
            //else
            //    result = doc.SelectSingleNode("/xml/result_msg").InnerText;
            return result;
        }
        #endregion


        #region FuYouRefundQuery(富友退款申请进度查询)
        /// <summary>
        /// 富友退款申请进度查询
        /// </summary>
        /// <param name="refund_order_no">商户退款单号</param>
        /// <param name="mchnt_cd">商户号, 富友分配的商户号</param>
        /// <returns></returns>
        public string FuYouRefundQuery(string refund_order_no
            , string mchnt_cd = "")
        {

            Dictionary<string, string> map = new Dictionary<string, string>();
            map.Add("version", "1.0");
            map.Add("ins_cd", FuiouPayConst.ins_cd);
            map.Add("mchnt_cd", mchnt_cd);
            map.Add("term_id", FuiouPayConst.term_id);
            map.Add("refund_order_no", refund_order_no);
            map.Add("random_str", Guid.NewGuid().ToString("N"));
            map.Add("sign", "");

            string result = run(map, FuiouPayConst.fuiou_30_url);
            result = GetXMLString(result);
            //XmlDocument doc = new XmlDocument();
            //doc.LoadXml(result);

            //string ifok = doc.SelectSingleNode("/xml/result_code").InnerText;
            //if (ifok == "000000")
            //    result = doc.SelectSingleNode("/xml/reserved_refund_amt").InnerText;
            //else
            //    result = doc.SelectSingleNode("/xml/result_msg").InnerText;
            return result;
        }
        #endregion


    }
}
